<!DOCTYPE html>
<html>
<head>
<title>InputEvent: beforeinput shouldn't crash in removed iframe</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
test(function() {
    assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
    assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');

    function testBeforeInputCrash(expectedBeforeInputCount, beforeInputTrigger, comments) {
        const iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        const childDocument = iframe.contentDocument;
        childDocument.body.innerHTML = '<p id="editable" contenteditable>Foo</p>';

        var actualBeforeInputCount = 0;
        const editable = childDocument.getElementById('editable');
        editable.addEventListener('beforeinput', event => {
            actualBeforeInputCount++;
            if (actualBeforeInputCount == expectedBeforeInputCount && iframe.parentNode)
                iframe.remove();
        });

        editable.focus();
        beforeInputTrigger(childDocument, editable, iframe.contentWindow.testRunner);

        if (iframe.parentNode)
            iframe.remove();
        assert_equals(actualBeforeInputCount, expectedBeforeInputCount, comments);
    }

    // Text command.
    testBeforeInputCrash(1, () => eventSender.keyDown('a'), 'Testing insertText "a"');
    testBeforeInputCrash(1, () => eventSender.keyDown('Enter', ['shiftKey']), 'Testing insertLineBreak');
    testBeforeInputCrash(1, () => eventSender.keyDown('Delete'), 'Testing deleteCharacterForward');

    // Styling command.
    testBeforeInputCrash(1, (childDocument, editable, testRunner) => {
        var selection = childDocument.getSelection();
        selection.collapse(editable, 0);
        selection.extend(editable, 1);
        testRunner.execCommand('bold');
    }, 'Testing bold');

    // Cut & Paste.
    testBeforeInputCrash(1, (childDocument, editable, testRunner) => {
        var selection = childDocument.getSelection();
        selection.collapse(editable, 0);
        selection.extend(editable, 1);
        eventSender.keyDown('Cut');
    }, 'Testing deleteByCut');
    testBeforeInputCrash(1, () => eventSender.keyDown('Paste'), 'Testing insertFromPaste');

    // Undo & Redo.
    testBeforeInputCrash(2, (childDocument, editable, testRunner) => {
        eventSender.keyDown('a');
        testRunner.execCommand('undo');
    }, 'Testing undo');
    testBeforeInputCrash(3, (childDocument, editable, testRunner) => {
        eventSender.keyDown('a');
        testRunner.execCommand('undo');
        testRunner.execCommand('redo');
    }, 'Testing redo');
}, 'Testing beforeinput in removed iframe');

async_test(function(t) {
    assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
    assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');

    function simulateDragDrop(dragElement, dropElement) {
        eventSender.mouseMoveTo(dragElement.offsetLeft + dragElement.offsetWidth / 2,
                                dragElement.offsetTop + dragElement.offsetHeight / 2);
        eventSender.mouseDown();
        eventSender.leapForward(100);
        eventSender.mouseMoveTo(dropElement.offsetLeft + dropElement.offsetWidth / 2,
                                dropElement.offsetTop + dropElement.offsetHeight / 2);
        eventSender.mouseUp();
    }

    function testDragDropCrash(inputTypeToCrash, comments, next) {
        const iframe = document.createElement('iframe');
        iframe.src = '../resources/beforeinput-remove-iframe-crash-iframe.html';
        iframe.onload = t.step_func(() => {
            var didFireInputTypeToCrash = false;
            const childDocument = iframe.contentDocument;
            const editable1 = childDocument.getElementById('editable1');
            const editable2 = childDocument.getElementById('editable2');
            childDocument.addEventListener('beforeinput', event => {
                if (event.inputType == inputTypeToCrash) {
                    didFireInputTypeToCrash = true;
                    if (iframe.parentNode)
                        iframe.remove();
                }
            });

            simulateDragDrop(editable1, editable2);

            if (iframe.parentNode)
                iframe.remove();
            assert_true(didFireInputTypeToCrash, comments);
            next();
        });
        document.body.appendChild(iframe);
    }

    // Test Drag&Drop.
    t.step(() => testDragDropCrash('deleteByDrag', 'Testing deleteByDrag',
        () => testDragDropCrash('insertFromDrop', 'Testing insertFromDrop',
        () => t.done())));
}, 'Testing beforeinput for Drag&Drop in removed iframe');
</script>
</body>
</html>
